﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Security.Cryptography;
using com.lover.common.json;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Net;

namespace com.lover.common
{
    public class CommonUtils
    {
        [DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool InternetSetCookie(string lpszUrlName, string lbszCookieName, string lpszCookieData);
        public static void setCookie(string url, string name, string value, string path, string domain)
        {
            string cookstr = String.Format("{0}={1};path={2};domain={3};", name, value, path, domain);
            InternetSetCookie(url, null, cookstr);
        }
        public static void setCookie(string url, string name, string value, string path, string domain, DateTime expires)
        {
            string cookstr = String.Format("{0}={1};path={2};domain={3};expires={4}", name, value, path, domain, expires.ToString("R"));
            InternetSetCookie(url, null, cookstr);
        }
        public static List<Cookie> getCookieByUrl(List<Cookie> all_cookies, string url)
        {
            List<Cookie> result_cookies = new List<Cookie>();
            if(all_cookies == null || all_cookies.Count == 0) return result_cookies;
            Uri uri = new Uri(url);
            string path = uri.AbsolutePath;
            string domain = uri.Host;
            foreach (Cookie cook in all_cookies)
            {
                if (path.StartsWith(cook.Path) && domain.EndsWith(cook.Domain)) result_cookies.Add(cook);
            }

            return result_cookies;
        }
        public static void updateCookies(ref List<Cookie> base_cookies, ref List<Cookie> new_cookies)
        {
            if (new_cookies == null || new_cookies.Count == 0) return;
            foreach (Cookie new_cook in new_cookies)
            {
                Cookie found_cookie = null;
                foreach (Cookie old_cook in base_cookies)
                {
                    if (old_cook.Name.Equals(new_cook.Name) && old_cook.Path.Equals(new_cook.Path)
                        && old_cook.Domain.Equals(new_cook.Domain))
                    {
                        found_cookie = old_cook;
                        break;
                    }
                }
                if (found_cookie != null) base_cookies.Remove(found_cookie);
                base_cookies.Add(new_cook);
            }
        }
        public static string DESEncrypt(string strPlain, string strDESKey, string strDESIV)
        {
            //把密钥转换成字节数组
            byte[] bytesDESKey = ASCIIEncoding.ASCII.GetBytes(strDESKey);
            //把向量转换成字节数组
            byte[] bytesDESIV = ASCIIEncoding.ASCII.GetBytes(strDESIV);
            //声明1个新的DES对象
            DESCryptoServiceProvider desEncrypt = new DESCryptoServiceProvider();
            //开辟一块内存流
            MemoryStream msEncrypt = new MemoryStream();
            //把内存流对象包装成加密流对象
            CryptoStream csEncrypt = new CryptoStream(msEncrypt, desEncrypt.CreateEncryptor(bytesDESKey, bytesDESIV), CryptoStreamMode.Write);
            //把加密流对象包装成写入流对象
            StreamWriter swEncrypt = new StreamWriter(csEncrypt);
            //写入流对象写入明文
            swEncrypt.WriteLine(strPlain);
            //写入流关闭
            swEncrypt.Close();
            //加密流关闭
            csEncrypt.Close();
            //把内存流转换成字节数组，内存流现在已经是密文了
            byte[] bytesCipher = msEncrypt.ToArray();
            //内存流关闭
            msEncrypt.Close();
            //把密文字节数组转换为字符串，并返回
            return UnicodeEncoding.Unicode.GetString(bytesCipher);
        }
        /// <summary>
        /// DES解密方法
        /// </summary>
        /// <param name="strCipher">密文</param>
        /// <param name="strDESKey">密钥</param>
        /// <param name="strDESIV">向量</param>
        /// <returns>明文</returns>
        public static string DESDecrypt(string strCipher, string strDESKey, string strDESIV)
        {
            //把密钥转换成字节数组
            byte[] bytesDESKey = ASCIIEncoding.ASCII.GetBytes(strDESKey);
            //把向量转换成字节数组
            byte[] bytesDESIV = ASCIIEncoding.ASCII.GetBytes(strDESIV);
            //把密文转换成字节数组
            byte[] bytesCipher = UnicodeEncoding.Unicode.GetBytes(strCipher);
            //声明1个新的DES对象
            DESCryptoServiceProvider desDecrypt = new DESCryptoServiceProvider();
            //开辟一块内存流，并存放密文字节数组
            MemoryStream msDecrypt = new MemoryStream(bytesCipher);
            //把内存流对象包装成解密流对象
            CryptoStream csDecrypt = new CryptoStream(msDecrypt, desDecrypt.CreateDecryptor(bytesDESKey, bytesDESIV), CryptoStreamMode.Read);
            //把解密流对象包装成读出流对象
            StreamReader srDecrypt = new StreamReader(csDecrypt);
            //明文=读出流的读出内容
            string strPlainText = srDecrypt.ReadLine();
            //读出流关闭
            srDecrypt.Close();
            //解密流关闭
            csDecrypt.Close();
            //内存流关闭
            msDecrypt.Close();
            //返回明文
            return strPlainText;
        }
        public static List<Point> getAStarPath(int[,] grids, Point start, Point target)
        {
            List<Point> short_path = new List<Point>();
            if (grids[target.X, target.Y] == -1) return short_path;
            int row_total = grids.GetLength(0), column_total = grids.GetLength(1);
            int[,] grid_f = new int[row_total,column_total];
            int[,] grid_g = new int[row_total, column_total];
            int[,] grid_h = new int[row_total, column_total];
            List<Point> open_list = new List<Point>();   // 保存所有的预备节点
            List<Point> open_list_parent = new List<Point>(); //保存所有的预备节点的父节点
            List<Point> closed_list = new List<Point>(); // 保存所有的已完结节点
            List<Point> closed_list_parent = new List<Point>(); // 保存所有的已完结节点的父节点

            bool reach = false;
            //!!!!! Point.X=row, Point.Y=column
            Point now = new Point(start.X, start.Y);
            grid_g[start.X, start.Y] = 0;
            grid_h[start.X, start.Y] = Math.Abs(target.X - start.X) + Math.Abs(target.Y - start.Y);
            grid_f[start.X, start.Y] = grid_g[start.X, start.Y] + grid_h[start.X, start.Y];
            closed_list.Add(new Point(start.X, start.Y));
            closed_list_parent.Add(new Point(start.X, start.Y));

            while (!reach)
            {
                if (now.X == target.X && now.Y == target.Y) { reach = true; break; }
                // 确定相邻格子的GHF值
                Point candidate = new Point(-1, -1);
                for (int i = 0; i < 4; i++)
                {
                    // left -> right -> top ->bottom
                    Point tmp = new Point();
                    int  tmp_g = 0, tmp_h = 0, tmp_f = 0, min_f = 99999999;
                    if (i == 0) { tmp.X = now.X; tmp.Y = now.Y - 1; }
                    else if (i == 1) { tmp.X = now.X; tmp.Y = now.Y + 1; }
                    else if (i == 2) { tmp.X = now.X - 1; tmp.Y = now.Y; }
                    else if (i == 3) { tmp.X = now.X + 1; tmp.Y = now.Y; }

                    // 如果节点不合法或者不能使用, 则跳过
                    if (tmp.X < 0 || tmp.Y < 0 || tmp.Y >= column_total || tmp.X >= row_total || grids[tmp.X, tmp.Y] == -1) continue;

                    // 判断tmp是否在closed_list中
                    bool in_closed_list = false;
                    foreach (Point p in closed_list)
                    {
                        if (p.X == tmp.X && p.Y == tmp.Y) { in_closed_list = true; break; }
                    }
                    if (in_closed_list) continue;

                    tmp_g = grid_g[now.X, now.Y] + 1;
                    tmp_h = Math.Abs(target.X - tmp.X) + Math.Abs(target.Y - tmp.Y);
                    tmp_f = tmp_g + tmp_h;

                    // 判断tmp是否在open_list中
                    bool in_open_list = false; int open_list_index = -1, tmp_index = -1;
                    foreach (Point p in open_list)
                    {
                        tmp_index++;
                        if (p.X == tmp.X && p.Y == tmp.Y) { in_open_list = true; open_list_index = tmp_index; break; }
                    }
                    if (in_open_list)
                    {
                        // 判断原来已经保存在该节点的G值是否比现在算出来的tmp_g小, 如果大, 那么还走现在的路径, 如果小, 那么将摒弃现在的路径, 直接从该节点的父节点走到该节点
                        if (grid_g[tmp.X, tmp.Y] < tmp_g)
                        {
                            // 查找该节点的父节点A在short_path中的位置, 将A之后的所有节点从short_path中删除
                            tmp_g = grid_g[tmp.X, tmp.Y];
                            tmp_h = grid_h[tmp.X, tmp.Y];
                            tmp_f = grid_f[tmp.X, tmp.Y];
                            if (tmp_f <= min_f) { candidate.X = tmp.X; candidate.Y = tmp.Y; }
                        }
                        else
                        {
                            grid_g[tmp.X, tmp.Y] = tmp_g;
                            grid_h[tmp.X, tmp.Y] = tmp_h;
                            grid_f[tmp.X, tmp.Y] = tmp_f;
                            open_list_parent[open_list_index] = new Point(now.X, now.Y);
                            if (tmp_f <= min_f) { candidate.X = tmp.X; candidate.Y = tmp.Y; }
                        }
                    }
                    else
                    {
                        open_list.Add(new Point(tmp.X, tmp.Y));
                        open_list_parent.Add(new Point(now.X, now.Y));

                        grid_g[tmp.X, tmp.Y] = tmp_g;
                        grid_h[tmp.X, tmp.Y] = tmp_h;
                        grid_f[tmp.X, tmp.Y] = tmp_f;
                        if (tmp_f <= min_f) { candidate.X = tmp.X; candidate.Y = tmp.Y; }
                    }
                }
                if (candidate.X < 0 || candidate.Y < 0) // candidate没有赋值, 意味着now这个点四周都不可走, 回退回去
                {
                    if (closed_list.Count == 1) // path中只有一个元素, 也就是start, 表示现在start位置周围都不可移动, 中断并返回列表
                    {
                        break;
                    }
                    else
                    {
                        // 判断now是否在closed_list中
                        int tmp_index = -1, tmp_now_index = -1;
                        foreach (Point p in closed_list)
                        {
                            tmp_index++;
                            if (p.X == now.X && p.Y == now.Y) { tmp_now_index = tmp_index; break; }
                        }
                        if (tmp_now_index < 0) now = closed_list[closed_list.Count - 1];
                        else if (tmp_now_index == 0) // 已经来到了起点, 则没有可用的路
                        {
                            break;
                        }
                        else now = closed_list[tmp_now_index - 1];
                    }
                }
                else
                {
                    // 将candidate从open_list中移除
                    int index = -1, candidate_index = -1;
                    foreach (Point p in open_list)
                    {
                        index++;
                        if (p.X == candidate.X && p.Y == candidate.Y) { candidate_index = index; break; }
                    }
                    Point parent = now;
                    if (candidate_index >= 0)
                    {
                        open_list.RemoveAt(candidate_index);
                        parent = open_list_parent[candidate_index];
                        open_list_parent.RemoveAt(candidate_index);
                    }
                    // 将candidate添加到closed_list
                    closed_list.Add(candidate);
                    closed_list_parent.Add(parent);
                    now.X = candidate.X; now.Y = candidate.Y;
                }
            }
            if (reach)
            {
                short_path.Insert(0, new Point(now.X, now.Y));
                int count = 0, total = row_total * column_total;
                while ((now.X != start.X || now.Y != start.Y) && count < total)
                {
                    int index = -1;
                    for (int i = 0; i < closed_list.Count; i++)
                    {
                        if (closed_list[i].X == now.X && closed_list[i].Y == now.Y) { index = i; break; }
                    }
                    if (index >= 0) now = closed_list_parent[index];
                    short_path.Insert(0, new Point(now.X, now.Y));
                    count++;
                }
            }
            return short_path;
        }
        public static string getReadable(long number)
        {
            string result = "";
            if (number > 1000000) result = String.Format("{0}万", (int)(number / 10000));
            else result = number.ToString();
            return result;
        }
        public static string generateBytesMd5(byte[] raw_bytes)
        {
            MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider();
            byte[] hash_bytes = provider.ComputeHash(raw_bytes);
            string md5str = BitConverter.ToString(hash_bytes);
            md5str = md5str.Replace("-", "");
            return md5str;
        }
        public static string generateStringMd5(string raw, Encoding encoding=null)
        {
            Encoding enc = Encoding.UTF8;
            if (encoding != null) enc = encoding;
            byte[] raw_bytes = enc.GetBytes(raw);
            MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider();
            byte[] hash_bytes = provider.ComputeHash(raw_bytes);
            string md5str = BitConverter.ToString(hash_bytes);
            md5str = md5str.Replace("-", "");
            return md5str;
        }
        private static List<string> _md5ExceptDir = null;
        public static JsonObjectCollection getLocalMd5(List<string> md5ExceptDir)
        {
            _md5ExceptDir = md5ExceptDir;
            string basedir = System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
            DirectoryInfo dirinfo = new DirectoryInfo(basedir);
            JsonObjectCollection obj = generateDirMd5(dirinfo, "");
            return obj;
        }

        private static string generateFileMd5(string filePath)
        {
            FileInfo finfo = new FileInfo(filePath);
            if (!finfo.Exists) return "";
            MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider();
            FileStream stream = finfo.OpenRead();
            byte[] hash_bytes = provider.ComputeHash(stream);
            stream.Close();
            string md5str = BitConverter.ToString(hash_bytes);
            md5str = md5str.Replace("-", "");
            return md5str;
        }

        private static JsonObjectCollection generateDirMd5(DirectoryInfo dirinfo, string dirPath)
        {
            JsonObjectCollection whole_jobj = new JsonObjectCollection();
            whole_jobj.Name = dirPath;
            
            string path = dirPath == "" ? "" : dirPath + "/";

            DirectoryInfo[] dirinfos = dirinfo.GetDirectories();
            for (int i = 0, len = dirinfos.Length; i < len; i++)
            {
                DirectoryInfo dinfo = dirinfos[i];
                if (dinfo.Name == "." || dinfo.Name == ".." || _md5ExceptDir.Contains(dinfo.Name)) continue;
                JsonObjectCollection sub_obj = generateDirMd5(dinfo, path + dinfo.Name);

                IEnumerator<JsonObject> sub_enumer = sub_obj.GetEnumerator();
                while (sub_enumer.MoveNext())
                {
                    JsonObject item = sub_enumer.Current;
                    whole_jobj.Add(item);
                }
            }

            FileInfo[] finfos = dirinfo.GetFiles();
            for (int i = 0, len = finfos.Length; i < len; i++)
            {
                FileInfo finfo = finfos[i];
                string md5str = generateFileMd5(finfo.FullName);
                md5str = md5str.ToLower();
                JsonStringValue fmd5 = new JsonStringValue();
                fmd5.Name = path + finfo.Name;
                fmd5.Value = md5str;
                whole_jobj.Add(fmd5);
            }
            return whole_jobj;
        }
    }
}
